package dipl.algorithm.math.fp.geometry.op;

import dipl.algorithm.math.fp.primitive.Point2df;
import dipl.algorithm.math.fp.primitive.Vector2df;
import dipl.algorithm.math.fp.primitive.op.Point2dOps;
import dipl.algorithm.math.primitive.Side;

/**
 * Operations on 2d-lines
 */
public class Line2dOps {

	//
	// PUBLIC METHODS
	//

	/**
	 * Tests for collinearity of line segment (p1,p2) and line segment (p3,p4)
	 * @param fpMath
	 * @param p1
	 * @param p2
	 * @param p3
	 * @param p4
	 * @return
	 */
	public static boolean LineSegmentsCollinear( Point2df p1, Point2df p2, Point2df p3, Point2df p4 ) {
		return( PointCollinearToLineSegment( p1, p3, p4 ) &&
						PointCollinearToLineSegment( p2, p3, p4 ) );
	}

	/**
	 * Tests whether line segment defined by points p1 and p2 intersects
	 * with line segment defined by points p3 and p4.
	 * @param fpMath
	 * @param p1
	 * @param p2
	 * @param p3
	 * @param p4
	 * @return
	 */
	public static boolean LineSegmentsIntersect( Point2df p1, Point2df p2, Point2df p3, Point2df p4 ) {
		double y12 = p1.y-p2.y, x21 = p2.x-p1.x;
		double y34 = p3.y-p4.y, x43 = p4.x-p3.x;

		double det34 = p3.x*p4.y-p3.y*p4.x;
		double det12 = p1.x*p2.y-p1.y*p2.x;

		double s1 = (p1.x*y34+p1.y*x43)+det34;
		double s2 = (p2.x*y34+p2.y*x43)+det34;
		double s3 = (p3.x*y12+p3.y*x21)+det12;
		double s4 = (p4.x*y12+p4.y*x21)+det12;

		return (Math.signum( s1 )*Math.signum( s2 ) < 0 && Math.signum( s3 )*Math.signum( s4 ) < 0);
	}

	/**
	 * Test whether point p is collinear with line segment (a,b)
	 * @param fpMath
	 * @param p
	 * @param a
	 * @param b
	 * @return
	 */
	public static boolean PointCollinearToLineSegment( Point2df p, Point2df a, Point2df b ) {
		double t0 = p.x*(a.y-b.y)+p.y*(b.x-a.x);
		double t1 = a.x*b.y-a.y*b.x;
		return (t0+t1) == 0.0;
	}

	/**
	 * Tests whether point p lies on line-segment specified by its endpoints a and b
	 * @param fpMath
	 * @param p
	 * @param a
	 * @param b
	 * @return
	 */
	public static boolean PointOnLineSegment( Point2df p, Point2df a, Point2df b ) {
		Point2df l1 = Point2dOps.ComparePointsLexicographically( a, b ) <= 0 ? a : b;
		Point2df l2 = l1 == a ? b : a;
		if( l1.x <= p.x && p.x <= l2.x && l1.y <= p.y && p.y <= l2.y ) {
			double t0 = p.x*(l1.y-l2.y)+p.y*(l2.x-l1.x);
			double t1 = l1.x*l2.y-l1.y*l2.x;
			return (t0+t1) == 0.0;
		}
		return false;
	}

	/**
	 * Calculates whether point p lies to the right, on or to the left of the directed line segment
	 * from a to b.
	 * If a = b the result will be Side.Onto
	 * @param fpMath
	 * @param p
	 * @param a
	 * @param b
	 * @return
	 */
	public static Side WhichSide( Point2df p, Point2df a, Point2df b ) {
		double t0 = p.x*(a.y-b.y)+p.y*(b.x-a.x);
		double t1 = a.x*b.y-a.y*b.x;
		double c = (t0+t1);
		if( c < 0.0 )
			return Side.Right;
		if( c > 0.0 )
			return Side.Left;
		return Side.Onto;
	}

	/**
	 * Calculates whether point p lies to the right, on or to the left of the directed line segment
	 * defined by vector v originating at point (0,0).
	 * @param fpMath
	 * @param p
	 * @param v
	 * @return
	 */
	public static Side WhichSide( Point2df p, Vector2df v ) {
		double c = p.x*v.y+p.y*v.x;
		if( c < 0.0 )
			return Side.Right;
		if( c > 0.0 )
			return Side.Left;
		return Side.Onto;
	}
}
